#include "mqclient61850.h"
#include <QDebug>
#include <QDateTime>

MQClient61850::MQClient61850()
{
    ClientInit();
}

void MQClient61850::ClientInit()
{
    char hostname[]=_IPV4_ADDR_;
    int tcpPort = _IPV4_PORT_;

    IedClientError error;

    m_iedConnectHandle = IedConnection_create();

    IedConnection_connect(m_iedConnectHandle, &error, hostname, tcpPort);

    if (error == IED_ERROR_OK) {

        printf("Get logical device list...\n");
        LinkedList deviceList = IedConnection_getLogicalDeviceList(m_iedConnectHandle, &error);

        if (error != IED_ERROR_OK) {
            printf("Failed to read device list (error code: %i)\n", error);
//            goto cleanup_and_exit;
        }

        LinkedList device = LinkedList_getNext(deviceList);

        while (device != NULL) {
            printf("LD: %s\n", (char*) device->data);
            _61850_LD_ t_ld;
            t_ld.name = (char*) device->data;

            LinkedList logicalNodes = IedConnection_getLogicalDeviceDirectory(m_iedConnectHandle, &error,
                    (char*) device->data);

            LinkedList logicalNode = LinkedList_getNext(logicalNodes);

            while (logicalNode != NULL) {
                printf("  LN: %s\n", (char*) logicalNode->data);
                _61850_LN_ t_ln;
                t_ln.name = (char*) logicalNode->data;

                char lnRef[101];
                sprintf(lnRef, "%s/%s", (char*) device->data, (char*) logicalNode->data);
                LinkedList dataObjects = IedConnection_getLogicalNodeDirectory(
                            m_iedConnectHandle, &error, lnRef, ACSI_CLASS_DATA_OBJECT);

                LinkedList dataObject = LinkedList_getNext(dataObjects);

                while (dataObject != NULL) {
                    // GET DO
                    char* dataObjectName = (char*) dataObject->data;

                    printf("    DO: %s\n", dataObjectName);
                    _61850_DO_ t_do;
                    t_do.type = "\tDO";
                    t_do.name = dataObjectName;

                    dataObject = LinkedList_getNext(dataObject);

                    // GET DA
                    char doRef[101];
                    sprintf(doRef, "%s/%s.%s", (char*) device->data, (char*) logicalNode->data, dataObjectName);
                    LinkedList dataAttributes = IedConnection_getDataDirectory(m_iedConnectHandle, &error, doRef);

                    if (dataAttributes != NULL) {
                        LinkedList dataAttribute = LinkedList_getNext(dataAttributes);

                        while (dataAttribute != NULL) {
                            char* daName = (char*) dataAttribute->data;
                            dataAttribute = LinkedList_getNext(dataAttribute);

                            //GET VALUE
                            char doRef2[101];
                            sprintf(doRef2, "%s.%s", doRef, daName);

//                            MmsValue* tmms_das = IedConnection_readObject(m_iedConnectHandle,
//                                                                       &error, doRef2,
//                                                                       FunctionalConstraint::IEC61850_FC_ALL);

                            _61850_DA_ t_da;
                            for(uint8_t i=0; i<=18; i++)
                            {
                                MmsValue* tmms_das = IedConnection_readObject(m_iedConnectHandle,
                                                                           &error, doRef2,
                                                                           (FunctionalConstraint)i);
                                QString str,val;
                                if(tmms_das!=NULL)
                                    if(MmsValue_getType(tmms_das)!=MMS_DATA_ACCESS_ERROR){
                                        _61850_DA_ t = GetDaStruct(tmms_das, doRef2);
                                        t.fc = QString(FunctionalConstraint_toString((FunctionalConstraint)i));
                                        t_da = t;
//                                        qDebug() << doRef2 << str << t.value;
                                    }
                            }

                            t_da.name = daName;
                            t_do.das.push_back(t_da);
                        }
                    }

                    t_ln.dos.push_back(t_do);
                }

                LinkedList_destroy(dataObjects);
                LinkedList dataSets = IedConnection_getLogicalNodeDirectory(
                            m_iedConnectHandle, &error, lnRef, ACSI_CLASS_DATA_SET);

                LinkedList dataSet = LinkedList_getNext(dataSets);

                while (dataSet != NULL) {

                    //GET Data Set
                    char* dataSetName = (char*) dataSet->data;
                    bool isDeletable;
                    char dataSetRef[130];
                    sprintf(dataSetRef, "%s.%s", lnRef, dataSetName);

//                    LinkedList dataSetMembers = IedConnection_getDataDirectory(m_iedConnectHandle, &error, dataSetRef);
                    LinkedList dataSetMembers = IedConnection_getDataSetDirectory(
                                m_iedConnectHandle, &error, dataSetRef, &isDeletable);

                    if (isDeletable) printf("    Data set: %s (deletable)\n", dataSetName);
                    else printf("    Data set: %s (not deletable)\n", dataSetName);

                    _61850_DATASET_ t_dataset;
                    t_dataset.type = "\tData set ";
                    t_dataset.type += isDeletable?" (deletable)":" (not deletable)";
                    t_dataset.name = dataSetName;

                    //GET FCDA name
                    LinkedList dataSetMemberRef = LinkedList_getNext(dataSetMembers);

                    while (dataSetMemberRef != NULL) {

                        char* memberRef = (char*) dataSetMemberRef->data;
                        printf("      %s\n", memberRef);

                        dataSetMemberRef = LinkedList_getNext(dataSetMemberRef);

                        _61850_FCDA_ t_fcda;
                        t_fcda.name = memberRef;
                        t_fcda.fc = t_fcda.name.mid(t_fcda.name.size()-3,2);
                        t_fcda.name = t_fcda.name.mid(0, t_fcda.name.size()-4);
                        t_dataset.fcdas.push_back(t_fcda);
                    }

                    //GET VALUE
                    ClientDataSet clientDataSet = IedConnection_readDataSetValues(m_iedConnectHandle, &error, dataSetRef, NULL);
                    if (error == IED_ERROR_OK) {
                        MmsValue* tmms_fcdas = ClientDataSet_getValues(clientDataSet);

                        for(int i=0; i<MmsValue_getArraySize(tmms_fcdas); i++)
                        {
                            MmsValue *tmms_fcda = MmsValue_getElement(tmms_fcdas, i);
                            //获取reference句柄
//                            char t_fcdaRef[130];
//                            sprintf(t_fcdaRef, "%s.%s", dataSetRef, t_dataset.fcdas[i].name.toLatin1().data());

                            _61850_FCDA_ t = GetFedaStruct(tmms_fcda, t_dataset.fcdas[i].name.toLatin1().data());
                            t_dataset.fcdas[i].type = MmsValue_getTypeString(tmms_fcda);
                            t_dataset.fcdas[i].value = t.value;
                            t_dataset.fcdas[i].structs = t.structs;
                        }
                        ClientDataSet_destroy(clientDataSet);
                    }

                    LinkedList_destroy(dataSetMembers);

                    dataSet = LinkedList_getNext(dataSet);

                    t_ln.dataset.push_back(t_dataset);
                }

                LinkedList_destroy(dataSets);
                LinkedList reports = IedConnection_getLogicalNodeDirectory(m_iedConnectHandle, &error, lnRef,
                        ACSI_CLASS_URCB);

                LinkedList report = LinkedList_getNext(reports);

                while (report != NULL) {
                    char* reportName = (char*) report->data;

                    printf("    RP: %s\n", reportName);
                    _61850_DO_ t_do;
                    t_do.type = "\tRP";
                    t_do.name = reportName;
                    t_ln.dos.push_back(t_do);

                    report = LinkedList_getNext(report);
                }

                LinkedList_destroy(reports);
                reports = IedConnection_getLogicalNodeDirectory(m_iedConnectHandle, &error, lnRef,
                        ACSI_CLASS_BRCB);

                report = LinkedList_getNext(reports);

                while (report != NULL) {
                    char* reportName = (char*) report->data;

                    printf("    BR: %s\n", reportName);
                    _61850_DO_ t_do;
                    t_do.type = "\tBR";
                    t_do.name = reportName;
                    t_ln.dos.push_back(t_do);

                    report = LinkedList_getNext(report);
                }

                LinkedList_destroy(reports);

                logicalNode = LinkedList_getNext(logicalNode);

                t_ld.lns.push_back(t_ln);
            }

            LinkedList_destroy(logicalNodes);

            device = LinkedList_getNext(device);

            m_dirVec.push_back(t_ld);
        }
        printf("\r\n");

        LinkedList_destroy(deviceList);

        IedConnection_close(m_iedConnectHandle);
    }
}

void MQClient61850::ClientUpdate()
{
    char hostname[]=_IPV4_ADDR_;
    int tcpPort = _IPV4_PORT_;

    IedClientError error;

    m_iedConnectHandle = IedConnection_create();

    IedConnection_connect(m_iedConnectHandle, &error, hostname, tcpPort);

    if (error == IED_ERROR_OK) {

        for(int i_ld=0; i_ld<m_dirVec.size(); i_ld++)
        {
            _61850_LD_ &t_ld = m_dirVec[i_ld];
            for(int i_ln=0; i_ln<t_ld.lns.size(); i_ln++)
            {
                _61850_LN_ &t_ln = t_ld.lns[i_ln];
                for(int i_do=0; i_do<t_ln.dos.size(); i_do++)
                {
                    _61850_DO_ &t_do = t_ln.dos[i_do];
                    for(int i_da=0; i_da<t_do.das.size(); i_da++)
                    {
                        _61850_DA_ &t_da = t_do.das[i_da];
                        char daRef[101];
                        sprintf(daRef, "%s/%s.%s.%s",
                                t_ld.name.toLatin1().data(), t_ln.name.toLatin1().data(),
                                t_do.name.toLatin1().data(), t_da.name.toLatin1().data());
                        MmsValue* tmms_das = IedConnection_readObject(m_iedConnectHandle,
                                                                   &error, daRef,
                                                                   FunctionalConstraint_fromString(t_da.fc.toLatin1().data()));
                        _61850_DA_ t = GetDaStruct(tmms_das, daRef);
                        t.fc = t_da.fc;
                        t.name = t_da.name;
                        t_da = t;
                    }
                }
            }
        }

        IedConnection_close(m_iedConnectHandle);
    }
}

void MQClient61850::WriteVal(QString locate, int type_num, QString type_extra, QVariant valueVar)
{
    char hostname[]=_IPV4_ADDR_;
    int tcpPort = _IPV4_PORT_;
    MmsValue* val;
    IedClientError err;

//    m_iedConnectHandle = IedConnection_create();

    IedConnection_connect(m_iedConnectHandle, &err, hostname, tcpPort);

    if (err == IED_ERROR_OK) {

        switch (type_num) {
        case MmsType::MMS_INTEGER:
            val = MmsValue_newInteger(valueVar.toInt());
            break;
        case MmsType::MMS_UTC_TIME:
            val = MmsValue_newUtcTime(valueVar.toInt());
            break;
        case MmsType::MMS_VISIBLE_STRING:
            val = MmsValue_newVisibleString(valueVar.toString().toLatin1().data());
            break;
        }
        if(val!=NULL)
            IedConnection_writeObject(m_iedConnectHandle, &err, locate.toLatin1().data(),
                                      FunctionalConstraint_fromString(type_extra.toLatin1().data()), val);
    }

    if (err != IED_ERROR_OK)
        qDebug() << "failed to write " << locate << " (error code: "  << err;

    if(val!=NULL) MmsValue_delete(val);

    IedConnection_close(m_iedConnectHandle);

}

_61850_FCDA_ MQClient61850::GetFedaStruct(MmsValue* tmms_fcda)
{
    _61850_FCDA_ t_fcda;
    t_fcda.type = MmsValue_getTypeString(tmms_fcda);
    t_fcda.type_num = MmsValue_getType(tmms_fcda);
    t_fcda.name = "[--]";
    switch(MmsValue_getType(tmms_fcda))
    {
    case MmsType::MMS_BOOLEAN:
        t_fcda.value = MmsValue_getBoolean(tmms_fcda)?"true":"false";
        break;
    case MmsType::MMS_FLOAT:
        t_fcda.value = QString::number(MmsValue_toFloat(tmms_fcda));
        break;
    case MmsType::MMS_INTEGER:
        t_fcda.value = QString::number(MmsValue_toInt32(tmms_fcda));
        break;
    case MmsType::MMS_UTC_TIME:
        t_fcda.value += QDateTime::fromMSecsSinceEpoch(MmsValue_getUtcTimeInMs(tmms_fcda)).toString("yyyy-MM-dd hh:mm:ss.zzz");
        break;
    case MmsType::MMS_BIT_STRING:
        t_fcda.value = QString::number(MmsValue_getBitStringAsInteger(tmms_fcda))+"[";
    {
        int size = MmsValue_getBitStringSize(tmms_fcda);
        for (int i=0; i<size; i++)
        {
            t_fcda.value += QString::number(MmsValue_getBitStringBit(tmms_fcda, i));
        }
        t_fcda.value += "]";
    }
        break;
    case MmsType::MMS_UNSIGNED:
        t_fcda.value = QString::number(MmsValue_toUint32(tmms_fcda));
        break;
    case MmsType::MMS_VISIBLE_STRING:
        t_fcda.value = QString(MmsValue_toString(tmms_fcda));
        break;
    case MmsType::MMS_OCTET_STRING:
        t_fcda.value = QString((char*)MmsValue_getOctetStringBuffer(tmms_fcda));
        break;
    case MmsType::MMS_STRUCTURE:
        for(int j=0; j<MmsValue_getArraySize(tmms_fcda); j++)
        {
            MmsValue *tmms_fcda_struct_singal = MmsValue_getElement(tmms_fcda, j);
            _61850_FCDA_ t_fcda2 = GetFedaStruct(tmms_fcda_struct_singal);
            t_fcda.structs.push_back(t_fcda2);
        }
        break;
    }
    return t_fcda;
}

_61850_FCDA_ MQClient61850::GetFedaStruct(MmsValue *tmms_fcda, char *reference)
{
    _61850_FCDA_ t_fcda;
    t_fcda.type = MmsValue_getTypeString(tmms_fcda);
    t_fcda.type_num = MmsValue_getType(tmms_fcda);
    t_fcda.name = "[--]";
    switch(MmsValue_getType(tmms_fcda))
    {
    case MmsType::MMS_BOOLEAN:
        t_fcda.value = MmsValue_getBoolean(tmms_fcda)?"true":"false";
        break;
    case MmsType::MMS_FLOAT:
        t_fcda.value = QString::number(MmsValue_toFloat(tmms_fcda));
        break;
    case MmsType::MMS_INTEGER:
        t_fcda.value = QString::number(MmsValue_toInt32(tmms_fcda));
        break;
    case MmsType::MMS_UTC_TIME:
        t_fcda.value += QDateTime::fromMSecsSinceEpoch(MmsValue_getUtcTimeInMs(tmms_fcda)).toString("yyyy-MM-dd hh:mm:ss.zzz");
        break;
    case MmsType::MMS_BIT_STRING:
        t_fcda.value = QString::number(MmsValue_getBitStringAsInteger(tmms_fcda))+"[";
    {
        int size = MmsValue_getBitStringSize(tmms_fcda);
        for (int i=0; i<size; i++)
        {
            t_fcda.value += QString::number(MmsValue_getBitStringBit(tmms_fcda, i));
        }
        t_fcda.value += "]";
    }
        break;
    case MmsType::MMS_UNSIGNED:
        t_fcda.value = QString::number(MmsValue_toUint32(tmms_fcda));
        break;
    case MmsType::MMS_VISIBLE_STRING:
        t_fcda.value = QString(MmsValue_toString(tmms_fcda));
        break;
    case MmsType::MMS_OCTET_STRING:
        t_fcda.value = QString((char*)MmsValue_getOctetStringBuffer(tmms_fcda));
        break;
    case MmsType::MMS_STRUCTURE:
    {

        IedClientError error;
        LinkedList t_dastructs = IedConnection_getDataDirectory(m_iedConnectHandle, &error, reference);
        LinkedList t_dastruct;
        if (t_dastructs != NULL) {
            t_dastruct = LinkedList_getNext(t_dastructs);
//            while (t_dastruct != NULL)
//            {
//                char* daStructName = (char*) t_dastruct->data;
//                t_dastruct = LinkedList_getNext(t_dastruct);
//            }
        }

        for(int j=0; j<MmsValue_getArraySize(tmms_fcda); j++)
        {
            //获取 MmsValue 数据结构体
            MmsValue *tmms_fcda_struct_singal = MmsValue_getElement(tmms_fcda, j);
            _61850_FCDA_ t_fcda2;
            if(t_dastruct==NULL)
                t_fcda2 = GetFedaStruct(tmms_fcda_struct_singal);
            else
            {
                //reference句柄
                char structRef[101];
                sprintf(structRef, "%s.%s", reference, (char*)t_dastruct->data);
                //递归轮询
                t_fcda2 = GetFedaStruct(tmms_fcda_struct_singal, structRef);
                //获取名称
                t_fcda2.name = QString((char*)t_dastruct->data);
                //指向下一个
                t_dastruct = LinkedList_getNext(t_dastruct);
            }
            //放入内存空间
            t_fcda.structs.push_back(t_fcda2);
        }
    }
        break;
    }
    return t_fcda;
}

_61850_DA_ MQClient61850::GetDaStruct(MmsValue* tmms_fcda)
{
    _61850_DA_ t_fcda;
    t_fcda.type = MmsValue_getTypeString(tmms_fcda);
    t_fcda.type_num = MmsValue_getType(tmms_fcda);
    t_fcda.name = "[--]";
    switch(MmsValue_getType(tmms_fcda))
    {
    case MmsType::MMS_BOOLEAN:
        t_fcda.value = MmsValue_getBoolean(tmms_fcda)?"true":"false";
        break;
    case MmsType::MMS_FLOAT:
        t_fcda.value = QString::number(MmsValue_toFloat(tmms_fcda));
        break;
    case MmsType::MMS_INTEGER:
        t_fcda.value = QString::number(MmsValue_toInt32(tmms_fcda));
        break;
    case MmsType::MMS_UTC_TIME:
//        t_fcda.value = QString::number(MmsValue_toUnixTimestamp(tmms_fcda));
//        t_fcda.value += QDateTime::fromSecsSinceEpoch(MmsValue_toUnixTimestamp(tmms_fcda)).toString(" yyyy-MM-dd hh:mm:ss.zzz");
//        t_fcda.value += QDateTime::fromMSecsSinceEpoch(Timestamp_getTimeInMs((Timestamp*)tmms_fcda->value.utcTime)).toString(" yyyy-MM-dd hh:mm:ss.zzz");
        t_fcda.value += QDateTime::fromMSecsSinceEpoch(MmsValue_getUtcTimeInMs(tmms_fcda)).toString("yyyy-MM-dd hh:mm:ss.zzz");
        break;
    case MmsType::MMS_BIT_STRING:
        t_fcda.value = QString::number(MmsValue_getBitStringAsInteger(tmms_fcda))+"[";
    {
        int size = MmsValue_getBitStringSize(tmms_fcda);
        for (int i=0; i<size; i++)
        {
            t_fcda.value += QString::number(MmsValue_getBitStringBit(tmms_fcda, i));
        }
        t_fcda.value += "]";
    }
        break;
    case MmsType::MMS_UNSIGNED:
        t_fcda.value = QString::number(MmsValue_toUint32(tmms_fcda));
        break;
    case MmsType::MMS_VISIBLE_STRING:
        t_fcda.value = QString(MmsValue_toString(tmms_fcda));
        break;
    case MmsType::MMS_OCTET_STRING:
//    {
//        uint8_t *t = MmsValue_getOctetStringBuffer(tmms_fcda);
//        int t2 = MmsValue_getOctetStringSize(tmms_fcda);
//        qDebug() << t;
//    }
        t_fcda.value = QString((char*)MmsValue_getOctetStringBuffer(tmms_fcda));
        break;
    case MmsType::MMS_STRUCTURE:
        for(int j=0; j<MmsValue_getArraySize(tmms_fcda); j++)
        {
            MmsValue *tmms_fcda_struct_singal = MmsValue_getElement(tmms_fcda, j);
            _61850_DA_ t_fcda2 = GetDaStruct(tmms_fcda_struct_singal);
            t_fcda.structs.push_back(t_fcda2);
        }
        break;
    }
    return t_fcda;
}

_61850_DA_ MQClient61850::GetDaStruct(MmsValue *tmms_fcda, char *reference)
{
    _61850_DA_ t_fcda;
    t_fcda.type = MmsValue_getTypeString(tmms_fcda);
    t_fcda.type_num = MmsValue_getType(tmms_fcda);
    t_fcda.name = "[--]";
    switch(MmsValue_getType(tmms_fcda))
    {
    case MmsType::MMS_BOOLEAN:
        t_fcda.value = MmsValue_getBoolean(tmms_fcda)?"true":"false";
        break;
    case MmsType::MMS_FLOAT:
        t_fcda.value = QString::number(MmsValue_toFloat(tmms_fcda));
        break;
    case MmsType::MMS_INTEGER:
        t_fcda.value = QString::number(MmsValue_toInt32(tmms_fcda));
        break;
    case MmsType::MMS_UTC_TIME:
        t_fcda.value += QDateTime::fromMSecsSinceEpoch(MmsValue_getUtcTimeInMs(tmms_fcda)).toString("yyyy-MM-dd hh:mm:ss.zzz");
        break;
    case MmsType::MMS_BIT_STRING:
        t_fcda.value = QString::number(MmsValue_getBitStringAsInteger(tmms_fcda))+"[";
    {
        int size = MmsValue_getBitStringSize(tmms_fcda);
        for (int i=0; i<size; i++)
        {
            t_fcda.value += QString::number(MmsValue_getBitStringBit(tmms_fcda, i));
        }
        t_fcda.value += "]";
    }
        break;
    case MmsType::MMS_UNSIGNED:
        t_fcda.value = QString::number(MmsValue_toUint32(tmms_fcda));
        break;
    case MmsType::MMS_VISIBLE_STRING:
        t_fcda.value = QString(MmsValue_toString(tmms_fcda));
        break;
    case MmsType::MMS_OCTET_STRING:
        t_fcda.value = QString((char*)MmsValue_getOctetStringBuffer(tmms_fcda));
        break;
    case MmsType::MMS_STRUCTURE:
    {
        IedClientError error;
        LinkedList t_dastructs = IedConnection_getDataDirectory(m_iedConnectHandle, &error, reference);
        LinkedList t_dastruct;
        if (t_dastructs != NULL) {
            t_dastruct = LinkedList_getNext(t_dastructs);
//            while (t_dastruct != NULL)
//            {
//                char* daStructName = (char*) t_dastruct->data;
//                t_dastruct = LinkedList_getNext(t_dastruct);
//            }
        }

        for(int j=0; j<MmsValue_getArraySize(tmms_fcda); j++)
        {
            //获取 MmsValue 数据结构体
            MmsValue *tmms_fcda_struct_singal = MmsValue_getElement(tmms_fcda, j);
            _61850_DA_ t_fcda2;
            if(t_dastruct==NULL)
                t_fcda2 = GetDaStruct(tmms_fcda_struct_singal);
            else
            {
                //reference句柄
                char structRef[101];
                sprintf(structRef, "%s.%s", reference, (char*)t_dastruct->data);
                //递归轮询
                t_fcda2 = GetDaStruct(tmms_fcda_struct_singal, structRef);
                //获取名称
                t_fcda2.name = QString((char*)t_dastruct->data);
                //指向下一个
                t_dastruct = LinkedList_getNext(t_dastruct);
            }
            //放入内存空间
            t_fcda.structs.push_back(t_fcda2);
        }
    }
        break;
    }
    return t_fcda;
}
